package com.dg.gaming.client.gui;

import java.awt.*;
import java.awt.event.*;
import java.util.*;

import javax.swing.*;

import info.clearthought.layout.TableLayout;
import nanoxml.XMLElement;
import com.dg.gaming.client.*;
import com.dg.gaming.api.common.*;
import com.dg.gaming.api.common.msg.*;
import com.dg.gaming.api.common.util.*;
import com.dg.gaming.api.common.playerstate.PlayerState;
import com.dg.gaming.api.common.playerstate.PlayerStateViewing;
import com.dg.gaming.api.common.playerstate.PlayerStateSeated;


public abstract class DGGameTable extends JFrame implements IClient,
        Observer, IGameClientGUI, IGameGUI {
    private static final int FRAME_HEIGHT = 435;
    private static final int FRAME_WIDTH = 590;
    // Declare constants
    private static final int WIDTH = 600;
    private static final int HEIGHT = 500;

    private int width = WIDTH;
    private int height = HEIGHT;

    private static final int SPACING = 5;
    private static final Font GAME_OVER_FONT = new Font("Arial", Font.BOLD, 20);
    private static final Color GAME_OVER_COLOUR = new Color(200, 0, 0);

    private String title; // title of this frame
    protected long username; // Username from conn.getUsername();
    protected TableConnection conn = null; // Connection
    protected String tableNum; // number of this table
    protected GameModel gameModel = null; // Client gameModel
    protected GameController gameController = null; // GameController
    protected DGComponent gameMainView = null; // main view
    protected Table table = null;
    protected Game game = null;
    protected Player player = null;

// Link to table hash in DGClientFrame
    private HashMap tableFramesHash = null;

// Declare GUI components
    private DGGlassPane glassPane;
    private DGPanel gamePanel, topPanel;
    private TimerPanel timerPanel;
    protected ChatTableComponent messageComponent;
    protected DGButton sitButton, standButton, startButton;
    protected DGButton offerDrawButton, resignButton, inviteButton;
    private JAvailableSeats availablePlayers;
    private JTablePlayers tablePlayers;
    private TableConnectionPanel connectionPanel = null;
    /**
     * Constructor to a standard DGGameFrame.  NOTE: Every contructor of a
     * sub class of this must call the setMVC (model, view, controller) method.
     *
     * @param conn	 Client connection thread which holds the link to the
     *               server, user and table lists.
     * @param width  Width of the table frame.
     * @param height Height of the table frame.
     */
    public DGGameTable() {
        this(WIDTH, HEIGHT);
    }

//    public static void main(String[] args) {
//        new DGGameTable();
//    }

//    public DGGameTable() {
//        super();
//        // Load images (in seperate thread)
//        GameImages.loadImages();
//
//        // Set the title of the client window.
//        this.setTitle(GameLabels.getClientTitle());
//
//        // Inform game properties this isn't an applet (i.e. is an application)
//        DGUtils.setApplet(false);
//
//        // Set size, move frame to center and make visible
//        Dimension size = DGAwt.getMinimumSize(this, FRAME_WIDTH,
//                                              FRAME_HEIGHT);
//        this.setSize(size);
//        Point location = DGAwt.getCentredLocation(this, size);
//        this.setLocation(location.x, location.y);
//        // Set convience fields
////        connectionPanel = new TableConnectionPanel(this);
////        getContentPane().add(connectionPanel);
////
////        // Set background colour of frame
////        getContentPane().setBackground(GameProperties.getBackgroundColour());
//
//        while (!GameImages.isFinishedLoading()) {}
//        setIconImage(GameImages.getImage("game.icon"));
//        setUpGUI();
//        // Make panel visible
//        setVisible(true);
//        validate();
//
//    }

    public DGGameTable(int width, int height) {
        super();
        // Set fields
        this.conn = conn;
        this.width = width;
        this.height = width;

        // Load images (in seperate thread)
        GameImages.loadImages();

        // Set the title of the client window.
        this.setTitle(GameLabels.getClientTitle());

        // Inform game properties this isn't an applet (i.e. is an application)
        DGUtils.setApplet(false);

        // Set size, move frame to center and make visible
        Dimension size = DGAwt.getMinimumSize(this, FRAME_WIDTH,
                                              FRAME_HEIGHT);
        this.setSize(size);
        Point location = DGAwt.getCentredLocation(this, size);
        this.setLocation(location.x, location.y);
        // Set convience fields
        connectionPanel = new TableConnectionPanel(this);
        getContentPane().add(connectionPanel);

        // Set background colour of frame
        getContentPane().setBackground(GameProperties.getBackgroundColour());

        while (!GameImages.isFinishedLoading()) {}
        setIconImage(GameImages.getImage("game.icon"));

        // Make panel visible
        setVisible(true);
        validate();

    }

    private void setUpGUI() {

        // Retrieve labels resources
        DGLabels resources = DGLabels.getInstance();

        // Set background colour
        getContentPane().setBackground(GameProperties.getBackgroundColour());

        // Create glass pane
        this.glassPane = new DGGlassPane();
        getRootPane().setGlassPane(glassPane);

        // Create main panel
        double pref = TableLayout.PREFERRED, fill = TableLayout.FILL;
        double[][] sizes = { {fill}, {50, pref, fill, pref}
        };
        DGPanel mainPanel = new DGPanel(sizes);
        mainPanel.setBorder(BorderFactory.createEtchedBorder());

        // Create top panel
        sizes = new double[][] { {pref, SPACING, pref}, {pref}
        };
        topPanel = new DGPanel(sizes);

        // Create game panel
        gamePanel = new DGPanel(new BorderLayout());

        // Create bottom panel (users cannot add to this).
        sizes = new double[][] { {0.3, 0.7}, {100}
        };
        DGPanel bottomPanel = new DGPanel(sizes);

        // Create buttons
        sizes = new double[][] { {SPACING, pref, SPACING, pref, SPACING, pref,
                SPACING, pref, SPACING}, {SPACING, pref, SPACING}
        };
        DGPanel firstPanel = new DGPanel(sizes);
        sizes = new double[][] { {SPACING, pref, SPACING, pref, SPACING, pref,
                SPACING, pref, SPACING}, {SPACING, pref, SPACING}
        };
        DGPanel secondPanel = new DGPanel(sizes);
        firstPanel.setBorder(BorderFactory.createEtchedBorder());
        secondPanel.setBorder(BorderFactory.createEtchedBorder());

        // Set up available players component
        if (table == null) {
            table = new Table("1", new User("", 1, 0, 0, 0, 0, 0), false,
                              new DGPropertyHash());
            player = table.getPlayerList().getPlayer(1L);
            table.setGame(new Game("1", 2, 2));

        }
        availablePlayers = new JAvailableSeats(player, table.getPlayerList(),
                                               table);

        // Set up buttons
        sitButton = new DGButton(resources.get("sit"));
        standButton = new DGButton(resources.get("stand"));
        startButton = new DGButton(resources.get("start"));
        offerDrawButton = new DGButton(resources.get("offer.draw"));
        resignButton = new DGButton(resources.get("resign"));
        inviteButton = new DGButton(resources.get("invite"));

        // Add to subPanels
        timerPanel = new TimerPanel(new JLabel(resources.get("playing.as")));
        firstPanel.add(timerPanel, "1,1");
        firstPanel.add(availablePlayers, "3,1");
        firstPanel.add(sitButton, "5,1");
        firstPanel.add(standButton, "7,1");
        secondPanel.add(startButton, "1,1");
        secondPanel.add(offerDrawButton, "3,1");
        secondPanel.add(resignButton, "5,1");
        secondPanel.add(inviteButton, "7,1");

        topPanel.add(firstPanel, "0,0");
        topPanel.add(secondPanel, "2,0");

        if (conn == null) {
            conn = new TableConnection(null, 1, this);
            conn.setTable(table);
        }
        // Create message component
        messageComponent = new ChatTableComponent(conn.getTableConn(), 3);

        // Create table players
        tablePlayers = new JTablePlayers(table.getPlayerList());
        DGPanel tablePlayersPanel = new DGPanel(new double[][] { {fill}, {fill}
        });
        tablePlayersPanel.setBorder(
                BorderFactory.createTitledBorder(
                        BorderFactory.createEtchedBorder(),
                        DGLabels.getInstance().get("user.list")
                )
                );
        DGScrollPane tablePlayersScroll = new DGScrollPane(tablePlayers);
        tablePlayersPanel.add(tablePlayersScroll, "0,0");

        // Add these components to bottom panel
        bottomPanel.add(tablePlayersPanel, "0,0");
        bottomPanel.add(messageComponent, "1,0");

        // Add to main panel
        mainPanel.add(new DGTitlePanel(), "0,0");
        mainPanel.add(topPanel, "0,1");
        mainPanel.add(gamePanel, "0,2");
        mainPanel.add(bottomPanel, "0,3");

        // Add the main panel to the content pane
        getContentPane().add(mainPanel);

        // Refresh state of buttons
        updateGuiStates();

        Dimension minSize = this.getPreferredSize();
        Dimension size = new Dimension
                         (
                                 Math.max((int) minSize.getWidth(), WIDTH),
                                 Math.max((int) minSize.getHeight(), HEIGHT)
                         );
        setSize(size);
        Dimension screenSize = getToolkit().getScreenSize();
        this.setLocation((int) screenSize.getWidth() / 2 - this.getWidth() / 2,
                         (int) screenSize.getHeight() / 2 -
                         this.getHeight() / 2);
        setVisible(true);
    }

    /**
     * Add listeners
     */
    private void addListeners() {
        //add close listener to frame
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                leaveTable();
            }
        });

        // Add listener for the sit button
        sitButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                sitDown();
            }
        });

        // Add listener for the stand button
        standButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                standUp();
            }
        });

        // Add listener for
        startButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                readyToStart();
            }
        });

        // Add listener for the offer draw button
        offerDrawButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                offerDraw();
            }
        });

        // Add listener for resign button
        resignButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                resign();
            }
        });

        // Add listener for invite button
        inviteButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                invite();
            }
        });

        // Add listeners on data
        table.addObserver(this); // 1) Table observer
        table.getPlayerList().addObserver(this); // 2) Player list observer
    }

    /**
     * Set the link to the table frames hash in the DGClientFrame (must
     * be deleted when the client is removed).
     *
     * @param tableFramesHash
     */
    public void setTableFramesHash(HashMap tableFramesHash) {
        this.tableFramesHash = tableFramesHash;
    }

    /**
     * Player leaves the table.
     */
    public void leaveTable() {
        // Check that the controller isn't null
        if (gameController != null) {

            // Check to see if the game is playing or not
            if (gameController.isGamePlaying() &&
                gameController.getSeatNum() != Player.NOT_SEATED) {
                if (JOptionPane.showConfirmDialog(
                        this,
                        "You cannot leave a table in progress.  Do you wish to resign?",
                        "Close Table",
                        JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
                    // Send resign method to server
                    MsgGameOver commGameOver = new MsgGameOver(IGameOver.
                            USER_RESIGNS);
                    conn.send(commGameOver);

                    // Now close the frame and removed any references to it
                    closeTableFrame();
                }
                return;
            }
        }

        // Now close the frame and removed any references to it
        closeTableFrame();
    }

    /**
     * Close the table frame.
     */
    private void closeTableFrame() {
        String tableNum = table.getTableNum();

        // Remove table from table hash in DGClientFrame
//        TableList tableList = conn.getTableList();
//        tableFramesHash.remove(new Integer(tableNum));
//        tableList.removePlayer(tableNum, username);
//        tableList.refreshObservers();

        // Inform other clients at table
        MsgExitTable commExitTable = new MsgExitTable();
        conn.send(commExitTable);

        // Set visible equal to false and dispose this frame
        setVisible(false);
        dispose();
    }

    /**
     * Set the empty panel in the screen equal to the game panel.
     *
     * @param gamePanel
     */
    public void setGamePanel(DGPanel gamePanel) {
        // Set up game panel (user must create this) in the middle
        this.gamePanel.add(gamePanel, BorderLayout.CENTER);
        validate();
        invalidate();
    }

    /**
     * Method to add additional components (such as buttons etc) to the top
     * right hand side of the screen.
     *
     * @param component
     */
    public void addToTopPanel(Component component) {
        topPanel.add(component);
    }

    /**
     * Accessor for the game controller.
     * @return
     */
    public GameController getController() {
        return gameController;
    }

    /**
     * Accessor for the game controller.
     *
     * @return
     */
    public GameModel getModel() {
        return gameModel;
    }

    /**
     * Accessor for the small message box with each table.
     *
     * @return
     */
    public ChatGameComponent getMessageComponent() {
        return messageComponent;
    }

    /**
     * Get invite dialog
     */
    public DGInviteDialog getInviteDialog() {
        return new DGInviteDialog(this,
                                  DGLabels.getInstance().get("invite"), true,
                                  conn.getTableConn());
    }

    /**
     * Return glass pane.
     *
     * @return
     */
    public DGGlassPane getDGGlassPane() {
        return this.glassPane;
    }

    /**
     * Set the DG glass pane.
     *
     * @param glassPane
     */
    public void setDGGlassPane(DGGlassPane glassPane) {
        this.glassPane = glassPane;
    }

    /**
     * Update the states of the buttons (done on a data change).
     */
    private void updateGuiStates() {
        if (table != null && player != null) {
            // Update the actual button
            sitButton.setEnabled(player.canSit(table));
            availablePlayers.setEnabled(player.canSit(table));
            standButton.setEnabled(player.canStand(table));
            startButton.setEnabled(player.canStart(table, conn.getGame()));
            offerDrawButton.setEnabled(player.canOfferDrawResign());
            resignButton.setEnabled(player.canOfferDrawResign());

            DGLabels labels = DGLabels.getInstance();
            if (player.isSeated() && !player.canStart(table, conn.getGame())) {
                glassPane.display(labels.get("waiting.on.other.players.to.sit"));
            } else if (player.canStart(table, conn.getGame())) {
                glassPane.display(labels.get("click.start.to.begin.game"));
            } else if (player.isReady() && !table.isGamePlaying()) {
                glassPane.display(labels.get(
                        "waiting.on.other.players.to.start"));
            } else {
                glassPane.setVisible(false);
            }
        }
    }

    /**
     * Implementation of the ITable interface.
     *
     * @see com.dg.gaming.client.gui.ITable#receiveTableMessage(nanoxml.XMLElement)
     */
    public void receiveTableMessage(XMLElement message, String tableNum) {
        try {
            String messageType = message.getName();
            Game game = table.getGame();
            long userid = conn.getUsername();
            if (messageType.equals(Msg.START_GAME)) {
                MsgStartGame commStartGame = new MsgStartGame(message);
                table.getPlayerList().setCurrentPlayer(commStartGame.
                        getCurrentPlayer());

                startGame(new MsgStartGame(message));
            } else if (messageType.equals(Msg.GAME_OVER)) {
                gameOver(new MsgGameOver(message));
            } else if (messageType.equals(Msg.OFFER_DRAW)) {
                receiveOfferDraw(new MsgOfferDraw(message));
            } else if (messageType.equals(Msg.CONTROLLER_PROPERTY)) {
                receiveProperty(new MsgControllerProperty(message));
            } else if (messageType.equals(Msg.CONTROLLER_OBJECT)) {
                receiveObject(new MsgControllerObject(message));
            } else if (messageType.equals(Msg.CHAT_TABLE)) {
                MsgChatTable commMessage = new MsgChatTable(message);
                receiveChatTableMessage(commMessage.getUserid(),
                                        commMessage.getTableNum(),
                                        commMessage.getChat());
            } else if (messageType.equals(Msg.JOIN_TABLE)) {
                MsgJoinTable commJoinTable = new MsgJoinTable(message);
                if (commJoinTable.containsModel()) {
                    gameModel.setState(commJoinTable.getModelState());
                }
                receiveJoinTable(new MsgJoinTable(message), userid, table);
            } else if (messageType.equals(Msg.EXIT_TABLE)) {
                MsgExitTable commExitTable = new MsgExitTable(message);
                game.getTableList().removePlayer(commExitTable.getTableNum(),
                                                 commExitTable.getUserid());
            } else if (messageType.equals(Msg.GAME_OVER)) {
                gameOver(new MsgGameOver(message), userid, table);
            } else if (messageType.equals(Msg.PLAYER_STATE)) {
                playerStateUpdate(new MsgPlayerState(message), table);
            } else if (messageType.equals(Msg.START_GAME)) {

            } else if (messageType.equals(Msg.NEXT_PLAYER)) {
                MsgNextPlayer commNextPlayer = new MsgNextPlayer(message);
                table.getPlayerList().setCurrentPlayer(commNextPlayer.getUserid());
            } else if (messageType.equals(Msg.TABLE_PROPERTY)) {
                MsgTableProperty commTableProperty = new MsgTableProperty(
                        message);
                table.addProperty(commTableProperty.getKey(),
                                  commTableProperty.getValue());
            } else if (messageType.equals(MsgNoMove.XML_ATT_NOMOVE)) {
                MsgNoMove nm = new MsgNoMove(message);
                noMove(nm);
            }

            // Also pass message into adapter method which all DG clients
            // can receive to do additional processing if this is required.
            receiveMessage(message);
        } catch (TransmissionException transEx) {}
    }

    /**
     * Receive a nomove table message.
     *
     * @param nomove  Join table object.
     */

    private void noMove(MsgNoMove nomove) {
        timerPanel.startTiming(nomove.getDeley());
    }

    /**
     * Receive a join table message.
     *
     * @param commJoinTable  Join table object.
     * @param username       Username.
     * @param table          Table object.
     */
    private void receiveJoinTable(MsgJoinTable commJoinTable, long userid,
                                  Table table) {

        PlayerList playerList = commJoinTable.getPlayerList();

        if (playerList == null) {
            // No player list, so just add the single player to the table.
            long joiningPlayerId = commJoinTable.getUserid();
            User joiningUser = game.getUserList().getUser(joiningPlayerId);
            if (joiningUser == null) {
                joiningUser = new User("", joiningPlayerId, 0, 0, 0, 0, 0);
                game.getUserList().addUser(joiningUser);
            }
            table.addPlayer(joiningUser);
        } else {
            // This message has a player list, so use that to set the playerlist
            // at the table.

            // First, Augment the player objects in the playerList with their users.
            playerList.updateUsers(game.getUserList());

            // Now, set the playerlist at the table
            table.setPlayerList(playerList);
        }

        // Update anything depending on tablelist
        game.getTableList().refreshObservers();
    }

    /**
     * Recieve a game over message and update the user objects.
     *
     * @param commGameOver  CommGameOver object.
     * @param username      Username object.
     * @param table         Table number.
     */
    private void gameOver(MsgGameOver commGameOver, long username,
                          Table table) {
        // Depending on the status create the game over message
        GameOver gameOver = commGameOver.getGameOver();
        long[] players = gameOver.getPlayers();
        int[] results = gameOver.getResults();
        int[] newRatings = gameOver.getNewRatings();

        // Update ratings of each player
        for (int i = 0; i < players.length; i++) {
            User user = game.getUserList().getUser(players[i]);
            user.update(results[i], newRatings[i]);
        }
        // Up$date table object.
        table.gameOver();

        game.getUserList().refreshObservers();
    }

    /**
     * Player state change i.e. a player is standing up / sitting down etc.
     *
     * @param commPlayerState
     */
    private void playerStateUpdate(MsgPlayerState commPlayerState, Table table) {
        // Retreive state change
        PlayerState commState = commPlayerState.getState();

        // Retreive player
        PlayerList commPlayers = table.getPlayerList();
        Player commPlayer = commPlayers.getPlayer(commPlayerState.getUserid());

        // update state on client player
        if (commState == null || commPlayer == null) {
            System.out.println();
        }
        commPlayer.setState(commState);

        // Check to see if the seat number needs updated.
        if (commState instanceof PlayerStateSeated) {
            commPlayer.setSeatNum(commPlayerState.getSeatNum());
        } else if (commState instanceof PlayerStateViewing) {
            commPlayer.setSeatNum(Player.NOT_SEATED);
        }

        commPlayers.refreshObservers();
    }

    /**
     * Adapter method for receiving a custom message.
     *
     * @see com.dg.gaming.client.gui.ITable#receiveTableMessage(nanoxml.XMLElement)
     */
    protected void receiveMessage(XMLElement message) {}

    /**
     * Receive start game message from server.  Change status of players, reset current
     * player (for turn based games), call controller.start() and startGame() on
     * any sub class of this class.
     *
     * @param commStartGame
     */
    private void startGame(MsgStartGame commStartGame) {
        // Start controller
        this.gameController.start(); // Controller is also informed game is started

        // Delegate to custom startGame () method in client if required
        this.startGame();
    }

    /**
     * Start the game (Empty adapter method).
     */
    public void startGame() {}

    /**
     * This client wishes to sit down at a particular table.
     */
    private void sitDown() {
        int selectedSeat = availablePlayers.getSelectedSeat();

        // Create table action communications object and send to server
        MsgSitDown commSitDown = new MsgSitDown(selectedSeat);
        conn.send(commSitDown);
    }

    /**
     * Stand back up again (status of player becomes VIEWING).
     */
    private void standUp() {
        // Set the status of the player back to VIEWING.
        MsgStandUp commStandUp = new MsgStandUp();

        conn.send(commStandUp);
    }

    /**
     * Player clicks the start button.
     */
    private void readyToStart() {
        MsgReadyToStart commReadyToStart = new MsgReadyToStart();
        conn.send(commReadyToStart);
    }

    /**
     * Offer the user a draw.
     */
    private void offerDraw() {
        // Create offer draw communications object and send to server.
        MsgOfferDraw commOfferDraw = new MsgOfferDraw(
                MsgInvite.REQUEST);
        conn.send(commOfferDraw);
    }

    /**
     * Receive offer draw.
     *
     * @param offerDraw
     */
    private void receiveOfferDraw(MsgOfferDraw offerDraw) {
        DGLabels dgLabels = DGLabels.getInstance();

        // Check to see if this is an offer draw or an offer draw reply
        if (offerDraw.getStatus() == MsgInvite.REQUEST) {

            String message =
                    dgLabels.get("player") + " " + offerDraw.getUserid() +
                    " " +
                    dgLabels.get("offers.a.draw") + " " +
                    dgLabels.get("accept") + "?";

            int answer = JOptionPane.showConfirmDialog(
                    this,
                    message,
                    dgLabels.get("table.message"),
                    JOptionPane.YES_NO_OPTION
                         );

            // Convert to a number the CommInvite object understands
            int yesNo = (answer == JOptionPane.YES_OPTION) ?
                        MsgInvite.ACCEPT : MsgInvite.DECLINE;

            // Create invite comm object to reply to the users
            MsgOfferDraw offerDrawReply = new MsgOfferDraw(
                    yesNo, offerDraw.getUserid());
            offerDrawReply.setSerialNum(offerDraw.getSerialNum());

            // Transmit message back to the server
            conn.send(offerDrawReply);
        } else { // is an offer draw reply from a user
            if (offerDraw.getStatus() == MsgInvite.DECLINE) {

                // report message that user has declined the offer
                String messageTitle =
                        "User " + offerDraw.getUserid() + " " +
                        dgLabels.get("has.rejected.draw.offer") + ".";

                JOptionPane.showMessageDialog(
                        this,
                        messageTitle,
                        dgLabels.get("table.message"),
                        JOptionPane.INFORMATION_MESSAGE
                        );
            }
        }
    }

    /**
     * Resign from this particular game.
     */
    private void resign() {
        // Player resigns
        MsgGameOver commGameOver = new MsgGameOver
                                   (IGameOver.USER_RESIGNS);
        conn.send(commGameOver);
    }

    /**
     * Pop up invite dialog and invite someone to the table
     */
    private void invite() {
        this.getInviteDialog();
    }

    /**
     * Sets up the MVC for this table.  This method <b>must</b> be called from
     * the constructor of any sub class of this class as this class refers to
     * the GameModel and DGController.
     *
     * @param model
     * @param mainView
     * @param controller
     */
    public void setupMVC(GameModel model, DGComponent mainView,
                         GameController controller) {
        this.gameModel = model;
        this.gameMainView = mainView;
        this.gameController = controller;
    }

    /**
     * Create game over message and add to chat box.
     *
     * @param commGameOver
     */
    private void gameOver(MsgGameOver commGameOver) {
        // Create game over message
        DGLabels labels = DGLabels.getInstance();
        StringBuffer fromSB, ratingsSB;

        // Depending on the status create the game over message
        GameOver gameOver = commGameOver.getGameOver();
        long[] players = gameOver.getPlayers();
        int[] results = gameOver.getResults();
        int[] oldRatings = gameOver.getOldRatings();
        int[] newRatings = gameOver.getNewRatings();

        // Loop through the players
        for (int i = 0; i < players.length; i++) {
            int result = results[i];

            // Create from message
            fromSB = new StringBuffer(
                    labels.get("player") + " " + players[i] + " ");
            if (result == IGameOver.DRAW) {
                fromSB.append(labels.get("draws"));
            } else if (result == IGameOver.LOSE) {
                fromSB.append(labels.get("loses"));
            } else if (result == IGameOver.WIN) {
                fromSB.append(labels.get("wins"));
            }

            // Create old / new ratings
            ratingsSB = new StringBuffer(
                    labels.get("old.rating") + " " + oldRatings[i] + " " +
                    labels.get("new.rating") + " " + newRatings[i]);

            // Write to the message component
            messageComponent.receiveMessage(fromSB.toString(),
                                            ratingsSB.toString());
        }

        // Display "Game Over" text in glass pane
        StringBuffer gameOverSB = new StringBuffer(labels.get("game.over"));
        int seatNum = player.getSeatNum();
        if (seatNum >= 0 && seatNum < players.length) {
            if (results[seatNum] == IGameOver.DRAW) {
                gameOverSB.append(" - " + labels.get("draw"));
            } else if (results[seatNum] == IGameOver.LOSE) {
                gameOverSB.append(" - " + labels.get("you.lose"));
            } else if (results[seatNum] == IGameOver.WIN) {
                gameOverSB.append(" - " + labels.get("you.win"));
            }
        }
        glassPane.display(GAME_OVER_FONT, GAME_OVER_COLOUR, gameOverSB.toString());
    }

    /**
     * Receive a property.
     *
     * @param commProperty
     */
    private void receiveProperty(MsgControllerProperty commProperty) {
        // retrieve controller from the correct table.
        GameController controller = getController();

        switch (commProperty.getStatus()) {
        case MsgControllerProperty.TYPE_STRING:
            controller.receiveProperty(commProperty.getKey(),
                                       commProperty.getValue());
            return;

        case MsgControllerProperty.TYPE_INT:
            int value = Integer.parseInt(commProperty.getValue());
            controller.receiveProperty(commProperty.getKey(), value);
            return;

        case MsgControllerProperty.TYPE_INT_TWO:
            StringTokenizer st = new StringTokenizer(commProperty.getValue());
            int value1 = Integer.parseInt(st.nextToken());
            int value2 = Integer.parseInt(st.nextToken());
            controller.receiveProperty(commProperty.getKey(), value1, value2);
            return;
        }
    }

    /**
     * Receive a message for a specificied table.
     *
     *
     * @param usernameFrom
     * @param tableNum
     * @param messageText
     */
    private void receiveChatTableMessage(long usernameFrom, String tableNum,
                                         String messageText) {
        getMessageComponent().receiveMessage(usernameFrom + "", messageText);
    }

    /**
     * Retrieve controller object.
     *
     * @param commObject
     */
    private void receiveObject(MsgControllerObject commObject) {
        GameController controller = getController();
        controller.receiveObject(commObject.getData());
    }

    /**
     * Update and refresh the buttons when the data changes.
     *
     * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void update(Observable obs, Object obj) {
        updateGuiStates();
    }

    public DGTableFrame getDGTableFrame(TableConnectionThread conn) {
        return null;
    }

    public void setUITitle(String title) {
    }

    public ChatPrivateDialog getChatPrivateDialog(long usernameTo,
                                                  ClientConnectionThread conn) {
        return null;
    }

    public void getPropertyDialog(ClientConnectionThread conn) {
    }

    public void getUserDialog(User user) {
    }

    public void getRulesDialog() {
    }

    public boolean hasExtendedInfo() {
        return false;
    }

    public String getExtendedTableInfoString(Table theTable) {
        return "";
    }

    public abstract GameModel getGameModel();

//    public GameModel getGameModel() {
//        return null;
//    };

    public void receiveGameMessage(XMLElement message) {
        receiveMessage(message);
        receiveTableMessage(message, tableNum);
    }

    public void connectOK(AbstractConnectionThread conn) {
        TableConnection cblCon = (TableConnection) conn;
        // Update client
        this.conn = cblCon;

        cblCon.setClientInterface(this);

        // Create new dg client frame

        getContentPane().remove(connectionPanel);
        this.tableNum = cblCon.getTableNum();
        this.table = cblCon.getTable();
        this.game = table.getGame();
        this.username = cblCon.getUsername();
        this.player = this.table.getPlayerList().getPlayer(username);

        // Set title
        this.title = GameLabels.getTableTitle() + " " +
                     tableNum + " - " +
                     DGLabels.getInstance().get("player") + ": " +
                     cblCon.getUsername();

        // Set title of frame
        setTitle(title);

        // Set up GUI
        setUpGUI();

        // Set image icon
        setIconImage(GameImages.getImage("game.icon"));

        // Add listeners
        addListeners();

        // Set size of the frame
        if (width != -1 && height != -1) {
            setSize(new Dimension(width, height));
        }

    }

}
